package service

import (
	"context"
	"iris-wms/apps/admin/web/dto/din"
	"iris-wms/apps/admin/web/dto/dout"
	"iris-wms/common/model"
	"iris-wms/common/pkg/db"
	"iris-wms/common/pkg/db/dao"
	"iris-wms/common/tools/consts"
	"math"

	"gorm.io/gorm"
)

var _ WmsBillPandianService = (*wmsBillPandianService)(nil)

type WmsBillPandianService interface {
	Create(din.WmsBillIn, consts.BillMark) error
	Update(din.WmsBillIn, consts.BillMark) error
	DeleteByIdList([]uint) error
	GetTable(din.WmsBillTableIn) ([]*dout.WmsBillOut, int64, error)
	FindByCode(string, consts.WorkType) (*dout.WmsBillOut, error)
	Over(din.WmsBillIn) error
	GetByGoodsIdListAndWid(din.WmsBillPandianQueryByGoodsIdListAndWidIn) ([]*dout.WmsReportOfGoodsOut, error)
}

func NewWmsBillPandianService() *wmsBillPandianService {
	return &wmsBillPandianService{}
}

type wmsBillPandianService struct{}

func (impl *wmsBillPandianService) Create(in din.WmsBillIn, mark consts.BillMark) (err error) {
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		for _, v := range in.GoodsList {
			v.BillId = in.Bill.ID
			v.Mark = mark
		}
		if err = tx.CreateInBatches(&in.GoodsList, len(in.GoodsList)).Error; err != nil {
			return err
		}
		return nil
	})
	return
}

func (impl *wmsBillPandianService) Update(in din.WmsBillIn, mark consts.BillMark) (err error) {
	count, err := impl.isOver(in.Bill)
	if err != nil {
		return err
	}
	if count > 0 {
		return consts.Wms_Bill_Pandian_Overed
	}

	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsBillGoods{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		for _, v := range in.GoodsList {
			v.BillId = in.Bill.ID
			v.Mark = mark
			if err = tx.Save(&v).Error; err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillPandianService) DeleteByIdList(idList []uint) (err error) {
	q := dao.Use(db.GDB1).WmsInventoryOut
	count, err := q.WithContext(context.Background()).Where(q.BillId.In(idList...)).Count()
	if err != nil {
		return err
	}
	if count > 0 {
		return consts.Wms_Bill_Pandian_Overed
	}

	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&model.WmsBill{}, "id in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Model(&model.WmsInventory{}).Where("bill_id in ?", idList).Update("status", 0).Error; err != nil {
			return err
		}
		return nil
	})
	return
}

func (impl *wmsBillPandianService) GetTable(in din.WmsBillTableIn) (rows []*dout.WmsBillOut, total int64, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())

	if in.Word != "" {
		do = do.Where(do.Where(q.Code.Eq(in.Word)))
	}
	rows, total, err = do.Where(q.WorkType.Eq(int8(in.WorkType))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.Supplier).Preload(q.Customer).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.Warehouse).
		Order(q.CreatedAt.Desc()).
		FindByPage(in.Offset(), in.PageSize)
	return
}

func (impl *wmsBillPandianService) FindByCode(code string, workType consts.WorkType) (bill *dout.WmsBillOut, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())
	bill, err = do.Where(q.Code.Eq(code), q.WorkType.Eq(int8(workType))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.Supplier).Preload(q.Customer).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.Warehouse).
		Preload(q.LogisticsList.Carrier).
		First()
	if err != nil {
		return nil, err
	}

	count, err := impl.isOver(&bill.WmsBill)
	if err != nil {
		return nil, err
	}
	if count > 0 {
		bill.Over = true
	}
	return
}

func (impl *wmsBillPandianService) Over(in din.WmsBillIn) (err error) {
	count, err := impl.isOver(in.Bill)
	if err != nil {
		return err
	}
	if count > 0 {
		return consts.Wms_Bill_Pandian_Overed
	}
	changed := func(goodsList []*model.WmsBillGoods) bool {
		for _, v := range goodsList {
			if v.PlanQty != v.Qty {
				return true
			}
		}
		return false
	}(in.GoodsList)
	if !changed {
		return consts.Wms_Bill_Pandian_Qty_Not_Changed
	}

	// inventoryService := NewWmsInventoryService()
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsBillGoods{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsInventory{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		for _, v := range in.GoodsList {
			// inventory, err := inventoryService.QueryInventory(v.GoodsId, v.Wid)
			// if err != nil {
			// 	if errors.Is(err, gorm.ErrRecordNotFound) {
			// 		return consts.Wms_Bill_Ountbound_Not_Qty
			// 	}
			// 	return err
			// }
			// if inventory.SumQty != v.PlanQty {
			// 	return consts.Wms_Bill_Pandian_Qty_Noteq
			// }

			v.BillId = in.Bill.ID
			if err = tx.Save(&v).Error; err != nil {
				return err
			}
			// invt
			diff := v.Qty - v.PlanQty
			if diff != 0 {
				invt := model.WmsInventory{
					BillId:  in.Bill.ID,
					Wid:     v.Wid,
					GoodsId: v.GoodsId,
					Qty:     math.Abs(diff),
					Price:   v.Price,
					Mark: func(value float64) consts.BillMark {
						if value > 0 {
							return consts.BillMarkIn
						} else {
							return consts.BillMarkOut
						}
					}(diff),
					IsGift: v.IsGift,
				}
				if err = tx.Save(&invt).Error; err != nil {
					return err
				}
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillPandianService) GetByGoodsIdListAndWid(in din.WmsBillPandianQueryByGoodsIdListAndWidIn) (rows []*dout.WmsReportOfGoodsOut, err error) {
	wmsInventoryService := NewWmsInventoryService()
	rows, err = wmsInventoryService.QueryInventoryOfGoodsIdListAndWid(in.GoodsIdLis, in.WidList)
	return
}

func (impl *wmsBillPandianService) isOver(bill *model.WmsBill) (int64, error) {
	q := dao.Use(db.GDB1).WmsInventoryOut
	count, err := q.WithContext(context.Background()).Where(q.BillId.Eq(bill.ID)).Count()
	if err != nil {
		return 0, err
	}
	return count, nil
}
